<!DOCTYPE html>
<html>
<head>
<title>Mandlebrot Viewer</title>
<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>
  function initialize() {
    if (!window.google || !google.maps) {
      warnOffline();
      return;
    }

    // Implements an ImageMapType
    // See https://developers.google.com/maps/documentation/javascript/maptypes#ImageMapTypes
    var mbTypeOptions = {
      getTileUrl: function(coord, zoom) {
        var nc = getNormalizedCoord(coord, zoom);
        if (!nc) {
          return null;
        }
        var url = '/tiles?z=' + zoom + '&x=' + nc.x + '&y=' + nc.y;
        if ({{.InProd}}) {
          // Shard across multiple domains so that browsers that are limited in
          // the number of in-flight requests per domain will load more quickly.
          // Compute the shard deterministically to be cache-friendly.
          var i = (nc.x + (2 * nc.y)) % 4;
          url = 'http://' + i + '.' + document.location.host + url;
        }
        return url;
      },
      tileSize: new google.maps.Size(256, 256),
      isPng: true,
      maxZoom: 15,
      minZoom: 0,
      name: 'Mandlebrot'
    };

    var mbMapType = new google.maps.ImageMapType(mbTypeOptions);

    var map = new google.maps.Map(document.getElementById('map_canvas'), {
      center: new google.maps.LatLng(0, 0),
      zoom: 1,
      mapTypeControlOptions: {
        mapTypeIds: ['mb']
      },
      streetViewControl: false
    });

    map.mapTypes.set('mb', mbMapType);
    map.setMapTypeId('mb');

    // Update the memcache stats every time a new set of tiles finishes loading.
    // Only do this if jQuery has succesfully loaded.
    if (window.jQuery) {
      google.maps.event.addListener(map, 'tilesloaded', updateStats);
    }
  }

  // Normalizes the coords that tiles repeat across the x axis (horizontally)
  // like the standard Google map tiles.
  function getNormalizedCoord(coord, zoom) {
    var y = coord.y;
    var x = coord.x;

    // tile range in one direction range is dependent on zoom level
    // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
    var tileRange = 1 << zoom;

    // don't repeat across y-axis (vertically)
    if (y < 0 || y >= tileRange) {
      return null;
    }

    // repeat across x-axis
    if (x < 0 || x >= tileRange) {
      x = (x % tileRange + tileRange) % tileRange;
    }

    return {
      x: x,
      y: y
    };
  }

  function updateStats() {
    var elem = $('#stats');

    // Asynchronously fetch /memcache-stats.
    $.getJSON('/memcache-stats', function(data) {
      if (data.error) {
        elem.text('Error: ' + data.error);
        return;
      }

      var arr = [];
      for (var key in data) {
        arr.push(key + ':' + data[key]);
      }
      elem.text('{ ' + arr.join(', ') + ' }');
    });
  }

  function warnOffline() {
    var mapElem = document.getElementById('map_canvas');
    mapElem.appendChild(document.createTextNode(
      'This example requires internet access to load ' +
      'the Google Maps API.'));
  }
  </script>
  <style type="text/css">
    body {
      font-family: Helvetica, sans-serif;
    }
  </style>
</head>
<body onload="initialize()">
  <div id="map_canvas" style="width: 640px; height: 480px;"></div>

  Powered by <a href="http://blog.golang.org/2011/05/go-and-google-app-engine.html">a horde of gophers</a>.

  <p id="stats" />
</body>
</html>
